`timescale 1ns/1ps
`default_nettype none

/*
* NOTE:
* 监测px口输入rgmii信号状态
*/

module px_check (
    input  wire         I_sclk,
    input  wire         I_rst_n,
    input  wire         I_ms_tick,
    input  wire         I_rxc,
    input  wire         I_rxdv,
    input  wire         I_rxer,
    output wire         O_error
);
//------------------------Parameter----------------------
localparam
    PACKET_MAX = 100,
    ERROR_MAX  = 20;

localparam [1:0]
    SPEED_1000M = 0,
    SPEED_100M  = 1,
    SPEED_10M   = 2,
    SPEED_NONE  = 3;

//------------------------Local signal-------------------
// rxer check
reg         rxdv_last;
reg         err_tmp;
reg  [6:0]  packet_num;
reg  [6:0]  error_num;
reg         error_flag0;

// speed check
reg         rxc_div;
reg  [3:0]  rxc_cnt;
reg  [3:0]  div_sync;
reg  [10:0] speed_cnt;
reg  [1:0]  speed;
reg  [6:0]  bad_speed_cnt;
reg         error_flag1;

//------------------------Instantiation------------------

//------------------------Body---------------------------
//{{{+++++++++++++++++++++rxer check+++++++++++++++++++++
// rxdv_last
always @(posedge I_rxc or negedge I_rst_n) begin
    if (!I_rst_n)
        rxdv_last <= 1'b0;
    else
        rxdv_last <= I_rxdv;
end

// err_tmp
always @(posedge I_rxc or negedge I_rst_n) begin
    if (!I_rst_n)
        err_tmp <= 1'b0;
    else if (!rxdv_last && I_rxdv)
        err_tmp <= 1'b0;
    else if (I_rxer)
        err_tmp <= 1'b1;
end

// packet_num
always @(posedge I_rxc or negedge I_rst_n) begin
    if (!I_rst_n)
        packet_num <= 1'b0;
    else if (rxdv_last && !I_rxdv)
        packet_num <= packet_num + 1'b1; 
    else if (packet_num == PACKET_MAX)
        packet_num <= 1'b0;
end

// error_num
always @(posedge I_rxc or negedge I_rst_n) begin
    if (!I_rst_n)
        error_num <= 1'b0;
    else if (rxdv_last && !I_rxdv && err_tmp)
        error_num <= error_num + 1'b1;
    else if (packet_num == PACKET_MAX)
        error_num <= 1'b0;
end

// error_flag0
always @(posedge I_rxc or negedge I_rst_n) begin
    if (!I_rst_n)
        error_flag0 <= 1'b0;
    else if (packet_num == PACKET_MAX) begin
        if (error_num > ERROR_MAX)
            error_flag0 <= 1'b1;
        else
            error_flag0 <= 1'b0;
    end
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++speed check++++++++++++++++++++
// rxc_div
always @(posedge I_rxc or negedge I_rst_n) begin
    if (!I_rst_n)
        rxc_div <= 1'b0;
    else if (rxc_cnt == 1'b0)
        rxc_div <= ~rxc_div;
end

// rxc_cnt
always @(posedge I_rxc or negedge I_rst_n) begin
    if (!I_rst_n)
        rxc_cnt <= 4'd15;
    else
        rxc_cnt <= rxc_cnt - 1'b1;
end

// div_sync
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        div_sync <= 1'b0;
    else
        div_sync <= {div_sync[2:0], rxc_div};
end

// speed_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        speed_cnt <= 1'b0;
    else if (div_sync[3] != div_sync[2]) // 上升沿或下降沿
        speed_cnt <= 1'b0;
    else if (!speed_cnt[10])
        speed_cnt <= speed_cnt + 1'b1;
end

// speed
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        speed <= SPEED_1000M;
    else if (speed_cnt[10])
        speed <= SPEED_NONE;
    else if (div_sync[3] != div_sync[2]) begin // 上升沿或下降沿
        if (speed_cnt < 10'd32)
            speed <= SPEED_1000M;
        else if (speed_cnt < 10'd160)
            speed <= SPEED_100M;
        else
            speed <= SPEED_10M;
    end
end

// bad_speed_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        bad_speed_cnt <= 7'd100;
    else if (speed == SPEED_1000M || speed == SPEED_10M)
        bad_speed_cnt <= 7'd100;
    else if (I_ms_tick && bad_speed_cnt != 1'b0)
        bad_speed_cnt <= bad_speed_cnt - 1'b1;
end

// error_flag1
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        error_flag1 <= 1'b0;
    else if (bad_speed_cnt == 1'b0)
        error_flag1 <= 1'b1;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++misc+++++++++++++++++++++++++++
assign O_error = error_flag0 | error_flag1;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

endmodule

`default_nettype wire

// vim: set ts=4 sw=4 et fenc=utf-8 fdm=marker:
